{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "from IPython.core.interactiveshell import InteractiveShell\n",
    "InteractiveShell.ast_node_interactivity = 'all'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1.可接受任意数量参数的函数\n",
    "\n",
    "为了能让一个函数接受任意数量的位置参数，可以使用一个`*`参数。\n",
    "\n",
    "为了接受任意数量的关键字参数，使用一个以`**`开头的参数。\n",
    "\n",
    "如果你还希望某个函数能同时接受任意数量的位置参数和关键字参数，可以同时使用`*`和`**`。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1.0"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "1.5"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "3.0"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def avg(first, *rest):\n",
    "    # rest是由所有其他位置参数组成的元组\n",
    "    return (first + sum(rest)) / (1 + len(rest))\n",
    "\n",
    "avg(1)\n",
    "avg(1, 2)\n",
    "avg(1, 2, 3, 4, 5)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'size': 'large', 'quantity': 6}\n",
      "size=large, quantity=6\n"
     ]
    }
   ],
   "source": [
    "def make_element(name, value, **attrs):\n",
    "    print(attrs)\n",
    "    keyvals = ['{}={}'.format(key, val) for key, val in attrs.items()]\n",
    "    # keyvals = [' %s=\"%s\"' % item for item in attrs.items()]\n",
    "    attr_str = ', '.join(keyvals)\n",
    "    print(attr_str)\n",
    "\n",
    "make_element('item', 'Albatross', size='large', quantity=6)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "('item', 'Albatross')\n",
      "{'size': 'large', 'quantity': 6}\n"
     ]
    }
   ],
   "source": [
    "def anyargs(*args, **kwargs):\n",
    "    print(args)\n",
    "    print(kwargs)\n",
    "\n",
    "anyargs('item', 'Albatross', size='large', quantity=6)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 一个*参数只能出现在函数定义中最后一个位置参数后面，而 **参数只能出现在最后一个参数。 \n",
    "# 有一点要注意的是，在*参数后面仍然可以定义其他参数。\n",
    "\n",
    "def a(x, *args, y):\n",
    "    pass\n",
    "\n",
    "def b(x, *args, y, **kwargs):\n",
    "    pass\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.只接受关键字参数的函数\n",
    "\n",
    "将强制关键字参数放到某个*参数或者单个*后面就能达到这种效果。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Receives a message\n",
      "Receives a message\n"
     ]
    },
    {
     "ename": "TypeError",
     "evalue": "recv() takes 1 positional argument but 2 were given",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[0;32m/tmp/ipykernel_3437986/234752063.py\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m      5\u001b[0m \u001b[0mrecv\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m1024\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mblock\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mTrue\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      6\u001b[0m \u001b[0mrecv\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m1024\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 7\u001b[0;31m \u001b[0mrecv\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m1024\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;32mTrue\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;31mTypeError\u001b[0m: recv() takes 1 positional argument but 2 were given"
     ]
    }
   ],
   "source": [
    "def recv(maxsize, *, block=None):\n",
    "    print('Receives a message')\n",
    "    pass\n",
    "\n",
    "recv(1024, block=True)\n",
    "recv(1024)\n",
    "recv(1024, True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "-5"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "0"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 我们还能在接受任意多个位置参数的函数中指定关键字参数。\n",
    "def minimum(*values, clip=None):\n",
    "    m = min(values)\n",
    "    if clip is not None:\n",
    "        m = clip if clip > m else m\n",
    "    return m\n",
    "\n",
    "minimum(1, 5, 2, -5, 10)\n",
    "minimum(1, 5, 2, -5, 10, clip=0)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3.给函数参数增加元信息\n",
    "\n",
    "- 函数参数注解"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Help on function add1 in module __main__:\n",
      "\n",
      "add1(x, y)\n",
      "\n",
      "Help on function add2 in module __main__:\n",
      "\n",
      "add2(x: int, y: int) -> int\n",
      "\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'x': int, 'y': int, 'return': int}"
      ]
     },
     "execution_count": 28,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def add1(x, y):\n",
    "    return x + y\n",
    "\n",
    "def add2(x:int, y:int) -> int:\n",
    "    return x + y\n",
    "\n",
    "help(add1)\n",
    "help(add2)\n",
    "\n",
    "# 函数注解只存储在函数的 __annotations__ 属性中。\n",
    "add2.__annotations__"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 4.返回多个值的函数\n",
    "为了能返回多个值，函数直接return一个元组就行了。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1"
      ]
     },
     "execution_count": 34,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "2"
      ]
     },
     "execution_count": 34,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "3"
      ]
     },
     "execution_count": 34,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "(1, 2, 3)"
      ]
     },
     "execution_count": 34,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def myfun():\n",
    "    return 1, 2, 3\n",
    "\n",
    "x, y, z = myfun()\n",
    "x\n",
    "y\n",
    "z\n",
    "\n",
    "a = myfun()\n",
    "a"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 5.定义有默认参数的函数\n",
    "\n",
    "定义一个有可选参数的函数是非常简单的，直接在函数定义中给参数指定一个默认值，并放到参数列表最后就行了。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1 42\n",
      "1 2\n",
      "1 55\n",
      "--------------------\n",
      "1 []\n",
      "2 3\n",
      "2 [3, 4]\n"
     ]
    }
   ],
   "source": [
    "def spam(a, b=42):\n",
    "    print(a, b)\n",
    "\n",
    "spam(1)\n",
    "spam(1, 2)\n",
    "spam(1, b=55)\n",
    "\n",
    "print('-'*20)\n",
    "# 如果默认参数是一个可修改的容器比如一个列表、集合或者字典，可以使用None作为默认值\n",
    "def spam(a, b=None):\n",
    "    if b is None:\n",
    "        b = []\n",
    "    print(a, b)\n",
    "\n",
    "spam(1)\n",
    "spam(2, 3)\n",
    "spam(2, [3, 4])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 6.定义匿名或内联函数\n",
    "\n",
    "- lambda表达式"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3"
      ]
     },
     "execution_count": 42,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "'helloworld'"
      ]
     },
     "execution_count": 42,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "add = lambda x, y: x + y\n",
    "\n",
    "add(1, 2)\n",
    "add('hello', 'world')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['Ned Batchelder', 'David Beazley', 'Raymond Hettinger', 'Brian Jones']"
      ]
     },
     "execution_count": 43,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# lambda表达式典型的使用场景是排序或数据reduce等\n",
    "\n",
    "names = ['David Beazley', 'Brian Jones','Raymond Hettinger', 'Ned Batchelder']\n",
    "sorted(names, key=lambda name: name.split()[-1].lower())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 7.匿名函数捕获变量值"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "30"
      ]
     },
     "execution_count": 44,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "30"
      ]
     },
     "execution_count": 44,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x = 10\n",
    "a = lambda y: x + y\n",
    "\n",
    "x = 20\n",
    "b = lambda y: x + y\n",
    "\n",
    "a(10)\n",
    "b(10)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "20"
      ]
     },
     "execution_count": 46,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "30"
      ]
     },
     "execution_count": 46,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 通过使用函数默认值参数形式，lambda函数在定义时就能绑定到值。\n",
    "x = 10\n",
    "a = lambda y, x=x: x + y\n",
    "\n",
    "x = 20\n",
    "b = lambda y, x=x: x + y\n",
    "\n",
    "a(10)\n",
    "b(10)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 8.减少可调用对象的参数个数\n",
    "\n",
    "如果需要减少某个函数的参数个数，你可以使用 functools.partial() 。 partial() 函数允许你给一个或多个参数设置固定的值，减少接下来被调用时的参数个数。 \n",
    "\n",
    "partial() 固定某些参数并返回一个新的callable对象。这个新的callable接受未赋值的参数， 然后跟之前已经赋值过的参数合并起来，最后将所有参数传递给原始函数。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "--------------------\n",
      "1 2 3 4\n",
      "1 4 5 6\n",
      "--------------------\n",
      "1 2 3 42\n",
      "4 5 6 42\n",
      "--------------------\n",
      "1 2 3 42\n",
      "1 2 5 42\n"
     ]
    }
   ],
   "source": [
    "def spam(a, b, c, d):\n",
    "    print(a, b, c, d)\n",
    "\n",
    "from functools import partial\n",
    "\n",
    "print('-'*20)\n",
    "s1 = partial(spam, 1)\n",
    "s1(2, 3, 4)\n",
    "s1(4, 5, 6)\n",
    "\n",
    "print('-'*20)\n",
    "s2 = partial(spam, d=42)\n",
    "s2(1, 2, 3)\n",
    "s2(4, 5, 6)\n",
    "\n",
    "print('-'*20)\n",
    "s3 = partial(spam, 1, 2, d=42)\n",
    "s3(3)\n",
    "s3(5)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 9.将单方法的类转换为函数\n",
    "\n",
    "你有一个除 __init__() 方法外只定义了一个方法的类。为了简化代码，你想将它转换成一个函数。\n",
    "\n",
    "大多数情况下，可以使用闭包来将单个方法的类转换成函数。\n",
    "\n",
    "略..."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 10.带额外状态信息的回调函数\n",
    "\n",
    "你的代码中需要依赖到回调函数的使用(比如事件处理器、等待后台任务完成后的回调等)， 并且你还需要让回调函数拥有额外的状态值，以便在它的内部使用到。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 11.内联回调函数\n",
    "\n",
    "没看懂，后面继续多读几遍。\n",
    "\n",
    "https://python3-cookbook.readthedocs.io/zh_CN/latest/c07/p11_inline_callback_functions.html#"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "当你编写使用回调函数的代码的时候，担心很多小函数的扩张可能会弄乱程序控制流。 你希望找到某个方法来让代码看上去更像是一个普通的执行序列。\n",
    "\n",
    "通过使用生成器和协程可以使得回调函数内联在某个函数中。 "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "starting...\n",
      "4\n",
      "********************\n",
      "res: None\n",
      "4\n"
     ]
    }
   ],
   "source": [
    "# 不使用send方法\n",
    "def foo():\n",
    "    print(\"starting...\")\n",
    "    while True:\n",
    "        res = yield 4\n",
    "        print(\"res:\",res)\n",
    "\n",
    "g = foo()\n",
    "print(next(g))\n",
    "print(\"*\"*20)\n",
    "print(next(g))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "starting...\n",
      "4\n",
      "********************\n",
      "res: 7\n",
      "4\n"
     ]
    }
   ],
   "source": [
    "# 使用send方法\n",
    "def foo():\n",
    "    print(\"starting...\")\n",
    "    while True:\n",
    "        res = yield 4\n",
    "        print(\"res:\",res)\n",
    "g = foo()\n",
    "print(next(g))\n",
    "print(\"*\"*20)\n",
    "print(g.send(7))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "5\n",
      "helloworld\n",
      "0\n",
      "2\n",
      "4\n",
      "6\n",
      "8\n",
      "Goodbye\n"
     ]
    }
   ],
   "source": [
    "# 好好读读这段代码\n",
    "# 除了那个特别的装饰器和 yield 语句外，其他地方并没有出现任何的回调函数(其实是在后台定义的)。\n",
    "from queue import Queue\n",
    "from functools import wraps\n",
    "\n",
    "def apply_async(func, args, *, callback):\n",
    "    result = func(*args)\n",
    "    callback(result)\n",
    "\n",
    "class Async:\n",
    "    def __init__(self, func, args):\n",
    "        self.func = func\n",
    "        self.args = args\n",
    "\n",
    "def inline_async(func):\n",
    "    @wraps(func)\n",
    "    def wrapper(*args):\n",
    "        f = func(*args)\n",
    "        result_queue = Queue()\n",
    "        result_queue.put(None)\n",
    "        while True:\n",
    "            result = result_queue.get()\n",
    "            try:\n",
    "                a = f.send(result)\n",
    "                apply_async(a.func, a.args, callback=result_queue.put)\n",
    "            except StopIteration:\n",
    "                break\n",
    "    \n",
    "    return wrapper\n",
    "\n",
    "\n",
    "def add(x, y):\n",
    "    return x + y\n",
    "\n",
    "@inline_async\n",
    "def test():\n",
    "    r = yield Async(add, (2, 3))\n",
    "    print(r)\n",
    "    \n",
    "    r = yield Async(add, ('hello', 'world'))\n",
    "    print(r)\n",
    "\n",
    "    for n in range(5):\n",
    "        r = yield Async(add, (n, n))\n",
    "        print(r)\n",
    "    print('Goodbye')\n",
    "\n",
    "\n",
    "test()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3.9.7 ('base')",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.7"
  },
  "orig_nbformat": 4,
  "vscode": {
   "interpreter": {
    "hash": "caf1c2fcf97217de91eafa76b907d50f9ea378f5ffbee7f571142d119bb6a771"
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
